﻿//
// Copyright 2008-2011 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//

using System;
using System.Collections;
using System.Diagnostics.Contracts;

namespace YaTools.Yaml
{
    public interface ITaggedObject : ICollection
    {
        Tag Tag { get; }
    }

    public sealed class TaggedScalar : ITaggedObject
    {
        [YamlTagProperty]
        public Tag Tag { get; private set; }
        public string Value { get; private set; }

        public TaggedScalar([YamlTagProperty] Tag tag, string value)
        {
            Contract.Requires(value != null);
            Value = value;
            Tag = tag;
        }

        public TaggedScalar(string value)
            : this(YaTools.Yaml.Tag.String, value)
        {
            Contract.Requires(value != null);
        }

        // ReSharper disable UnusedMember.Local
        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(Value != null);
        }

        // ReSharper restore UnusedMember.Local


        #region ITaggedObject Members

        void ICollection.CopyTo(Array array, int index)
        {
            Contract.Assume(array.GetUpperBound(0) >= 1);

            array.SetValue(Value, 0);
        }

        int ICollection.Count
        {
            get { return 1; }
        }

        bool ICollection.IsSynchronized
        {
            get { return false; }
        }

        object ICollection.SyncRoot
        {
            get { return Value; }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            yield return Value;
        }

        #endregion

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(obj, this)) return true;
            var other = obj as TaggedScalar;
            if (other != null && other.Tag == Tag && other.Value == Value)
                return true;
            var s = obj as string;
            if (Value.Equals(s)) return true;
            return false;
        }

        public override int GetHashCode()
        {
            Contract.Assume(Tag != null);
            return Value.GetHashCode() ^ Tag.GetHashCode();
        }

        public override string ToString()
        {
            return Value;
        }
    }

    [Serializable]
    public class TaggedDictionary : Hashtable, ITaggedObject
    {
        public TaggedDictionary([YamlTagProperty] Tag tag)
        {
            Tag = tag;
        }

        [YamlTagProperty]
        public Tag Tag { get; private set; }
    }

    public class TaggedSequence : ArrayList, ITaggedObject
    {
        public TaggedSequence([YamlTagProperty] Tag tag)
        {
            Tag = tag;
        }

        [YamlTagProperty]
        public Tag Tag { get; private set; }
    }
}